//
// Created by mmuee on 2023/2/1.
//

#ifndef LOGER_HPP
#define LOGER_HPP

#include <string>
#include <cstdarg>
#include <sstream>
#include <list>
#include <atomic>
#include <memory>
#include <mutex>
#include <fstream>

#ifndef __FILE_NAME__
#define __FILE_NAME__ __FILE__
#define FIX_FILE_NAME 1
#endif

#define LOG_MACRO(content, flag)                                               \
    Loger::shared()->hasLogFlag(flag) &&                                     \
        __Loger() == LogRecord(content, flag, __LINE__, __FILE__,             \
                                __FILE_NAME__, __FUNCTION__)

#define LOG_VERBOSE(...) LOG_MACRO(logFormat(__VA_ARGS__), LogFlagVerbose)

#define LOG_DEBUG(...) LOG_MACRO(logFormat(__VA_ARGS__), LogFlagDebug)

#define LOG_INFO(...) LOG_MACRO(logFormat(__VA_ARGS__), LogFlagInfo)

#define LOG_WARN(...) LOG_MACRO(logFormat(__VA_ARGS__), LogFlagWarning)

#define LOG_ERROR(...) LOG_MACRO(logFormat(__VA_ARGS__), LogFlagError)

enum LogFlag
{
    /**
     *  0...00001 LogFlagError
     */
    LogFlagError = (1 << 0),

    /**
     *  0...00010 LogFlagWarning
     */
    LogFlagWarning = (1 << 1),

    /**
     *  0...00100 LogFlagInfo
     */
    LogFlagInfo = (1 << 2),

    /**
     *  0...01000 LogFlagDebug
     */
    LogFlagDebug = (1 << 3),

    /**
     *  0...10000 LogFlagVerbose
     */
    LogFlagVerbose = (1 << 4)
};

enum LogLevel
{
    /**
     *  No logs
     */
    LogLevelOff = 0,

    /**
     *  Error logs only
     */
    LogLevelError = (LogFlagError),

    /**
     *  Error and warning logs
     */
    LogLevelWarning = (LogLevelError | LogFlagWarning),

    /**
     *  Error, warning and info logs
     */
    LogLevelInfo = (LogLevelWarning | LogFlagInfo),

    /**
     * Error, warning, info and debug logs
     */
    LogLevelDebug = (LogLevelInfo | LogFlagDebug),

    /**
     * Error, warning, info, debug and verbose logs
     */
    LogLevelVerbose = (LogLevelDebug | LogFlagVerbose), /**
         *  All logs (1...11111)
         */
    LogLevelAll
};

std::string logFormat(const char* fmt, ...);

std::string logFormat();

struct LogRecord;

// 存储接口.
class LogStore
{
    friend class Loger;

public:
    virtual ~LogStore() = default;

protected:
    virtual void put(const LogRecord& record) = 0;
};

using LogStoreRef = std::shared_ptr<LogStore>;

// 日志.
class Loger
{
public:
    static Loger* shared();

    bool hasLogFlag(LogFlag flag);

    void setLogLevel(LogLevel level);

    void addRecord(const LogRecord& record);

    void addStore(const LogStoreRef& store);

    bool operator<<(const LogRecord&& record);

private:
    Loger();

    ~Loger();

    Loger(const Loger&) = delete;

    const Loger& operator=(const Loger&) = delete;

    std::atomic<LogLevel> level{LogLevelInfo};
    std::list<LogStoreRef> stores{};
};

struct __Loger
{
    bool operator==(LogRecord& record)
    {
        Loger::shared()->addRecord(record);
        return true;
    }

    bool operator==(LogRecord&& record)
    {
        Loger::shared()->addRecord(record);
        return true;
    }
};

// 日志记录
struct LogRecord
{
    LogFlag flag{};
    uint32_t line{};
    int64_t timestamp{};

    std::stringstream cstream{};
    std::string file{};
    std::string fileName{};
    std::string function{};
    std::string threadID{};
    std::string threadName{};

    const std::string& flag_name() const;

    std::string content() const;

    LogRecord(const std::string& content, LogFlag flag, uint32_t line,
               std::string file, std::string fileName, std::string function);

    LogRecord(LogRecord&&) = default;

    LogRecord& operator=(LogRecord&&) = default;

    template<typename Type>
    typename std::enable_if<std::is_same<Type, int8_t>::value ||
                                std::is_same<Type, int16_t>::value ||
                                std::is_same<Type, int32_t>::value ||
                                std::is_same<Type, int64_t>::value ||
                                std::is_same<Type, uint8_t>::value ||
                                std::is_same<Type, uint16_t>::value ||
                                std::is_same<Type, uint32_t>::value ||
                                std::is_same<Type, uint64_t>::value ||
                                std::is_same<Type, char>::value ||
                                std::is_same<Type, bool>::value ||
                                std::is_same<Type, const char*>::value ||
                                std::is_same<Type, void*>::value ||
                                std::is_same<Type, std::string>::value ||
                                std::is_same<Type, double>::value ||
                                std::is_same<Type, float>::value,
                            LogRecord&>::type
    operator<<(Type const& arg)
    {
        cstream << arg;
        return *this;
    }

    template<size_t N>
    LogRecord& operator<<(const char (&arg)[N])
    {
        cstream << arg;
        return *this;
    }
};

// 控制台输出.
class LogStoreStd : public LogStore
{
public:
    LogStoreStd(){};

    ~LogStoreStd(){};

protected:
    void put(const LogRecord& record) override;
};

//TODO: 文件输出.
class LogStoreFile : public LogStore
{
public:
    explicit LogStoreFile(std::string file)
        : outputStream(file, std::ios_base::out), outputFile(std::move(file)){};

    ~LogStoreFile(){};

protected:
    void put(const LogRecord& record) override;

    std::ofstream outputStream;
    std::string outputFile;
};

#endif //LOGER_HPP
